package com.ustc.hewei.snowflakemodule.snowFlake;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author hewei
 * @version 1.0
 * @description: 生成id主类
 * @date 2022/10/12 21:27
 */

public class SnowflakeWorker {
    private static final Logger logger = LoggerFactory.getLogger(SnowflakeWorker.class);
    // 1650789964886：2022-10-11 21:37
    private final long INIT_EPOCH = 1665495467047L;
    // 时间位取&
    private final long TIME_BIT = 0b1111111111111111111111111111111111111111110000000000000000000000L;
    // 记录最后使用的毫秒时间戳，主要用于判断是否同一毫秒，以及用于服务器时钟回拨判断
    private long lastTimeMillis = -1L;
    // dataCenterId占用的位数
    private final long DATA_CENTER_ID_BITS = 5L;
    // dataCenterId占用5个比特位，最大值31
    // 0000000000000000000000000000000000000000000000000000000000011111
    private final long MAX_DATA_CENTER_ID = ~(-1L << DATA_CENTER_ID_BITS);
    // dataCenterId
    private long dataCenterId;
    // workId占用的位数
    private final long WORKER_ID_BITS = 5L;
    // workId占用5个比特位，最大值31
    // 0000000000000000000000000000000000000000000000000000000000011111
    private final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);
    // workId
    private long workerId;
    // 最后12位，代表每毫秒内可产生最大序列号，即 2^12 - 1 = 4095
    private final long SEQUENCE_BITS = 12L;
    // 掩码（最低12位为1，高位都为0），主要用于与自增后的序列号进行位与，如果值为0，则代表自增后的序列号超过了4095
    // 0000000000000000000000000000000000000000000000000000111111111111
    private final long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS);
    // 同一毫秒内的最新序号，最大值可为 2^12 - 1 = 4095
    private long sequence;
    // workId位需要左移的位数 12
    private final long WORK_ID_SHIFT = SEQUENCE_BITS;
    // dataCenterId位需要左移的位数 12+5
    private final long DATA_CENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
    // 时间戳需要左移的位数 12+5+5
    private final long TIMESTAMP_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATA_CENTER_ID_BITS;

    public SnowflakeWorker(String ip, String port) {
        this(ip, "127.0.0.1:2181", port);
    }

    public SnowflakeWorker(String ip, String zkAddress, String port) {
        SnowflakeZkHandler snowflakeZkHandler = new SnowflakeZkHandler(zkAddress, ip, port);
        boolean res = snowflakeZkHandler.init();
        if (res) {
            workerId = snowflakeZkHandler.getWorkId();
        } else {
            logger.error("SnowflakeZkHandler初始化异常!");
        }
    }

    /**
     * 通过雪花算法生成下一个id，注意这里使用synchronized同步
     *
     * @return 唯一id
     */
    public synchronized long nextId() {
        long currentTimeMillis = System.currentTimeMillis();
        // 当前时间小于上一次生成id使用的时间，可能出现服务器时钟回拨问题
        if (currentTimeMillis < lastTimeMillis) {
            throw new RuntimeException(
                    String.format("可能出现服务器时钟回拨问题，请检查服务器时间。当前服务器时间戳：%d，上一次使用时间戳：%d", currentTimeMillis,
                            lastTimeMillis));
        }
        if (currentTimeMillis == lastTimeMillis) {
            // 还是在同一毫秒内，则将序列号递增1，序列号最大值为4095
            // 序列号的最大值是4095，使用掩码（最低12位为1，高位都为0）进行位与运行后如果值为0，则自增后的序列号超过了4095
            // 那么就使用新的时间戳
            sequence = (sequence + 1) & SEQUENCE_MASK;
            if (sequence == 0) {
                currentTimeMillis = getNextMillis(lastTimeMillis);
            }
        } else { // 不在同一毫秒内，则序列号重新从0开始，序列号最大值为4095
            sequence = 0;
        }
        // 记录最后一次使用的毫秒时间戳
        lastTimeMillis = currentTimeMillis;
        // 核心算法，将不同部分的数值移动到指定的位置，然后进行或运行
        return ((currentTimeMillis - INIT_EPOCH) << TIMESTAMP_SHIFT)
                // 数据中心部分
                | (dataCenterId << DATA_CENTER_ID_SHIFT)
                // 机器表示部分
                | (workerId << WORK_ID_SHIFT)
                // 序列号部分
                | sequence;
    }

    /**
     * 获取指定时间戳的接下来的时间戳，也可以说是下一毫秒
     *
     * @param lastTimeMillis 指定毫秒时间戳
     * @return 时间戳
     */
    private long getNextMillis(long lastTimeMillis) {
        long currentTimeMillis = System.currentTimeMillis();
        while (currentTimeMillis <= lastTimeMillis) {
            currentTimeMillis = System.currentTimeMillis();
        }
        return currentTimeMillis;
    }

}
